// UNIXSocketClient.cpp
//
// Copyright (c) 2008 Bojan Rajkovic
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
// THE SOFTWARE.
//
//

#include "../include/UNIXClientSocket.h"

UNIXClientSocket::UNIXClientSocket(std::string SocketPath) {

	this->clientSocket = socket(AF_UNIX, SOCK_STREAM, 0);
	
	if (this->clientSocket == -1) {
		throw SocketException(errno);	
	}
	
	this->clientSocketDescriptor = fdopen(this->clientSocket, "r+");
	setvbuf(this->clientSocketDescriptor, static_cast<char*>(NULL), 0, _IONBF);
	
	struct sockaddr_un remoteAddress;
	
	remoteAddress.sun_family = AF_UNIX;
	strcpy(remoteAddress.sun_path, SocketPath.c_str());
	int remoteAddressSize = strlen(remoteAddress.sun_path) + sizeof(remoteAddress.sun_family);
	
	if (connect(this->clientSocket, reinterpret_cast<struct sockaddr*>(&remoteAddress), remoteAddressSize) == -1) {
		throw SocketException(errno);
	}
	
	// Okay, we're connected
}

UNIXClientSocket::~UNIXClientSocket() {	
	fclose(this->clientSocketDescriptor);
	close(this->clientSocket);
	Utility::LogMessage("Destroying UNIXClientSocket.", M_INFO);
}

int UNIXClientSocket::Write(std::string Data) {
	// We'll just flat out send the data, there's not much to much aroud with here
	// Return the return value of send because it indicates how many bytes were sent
	// No need for flags, nothing applies.
	int retval = send(this->clientSocket, Data.c_str(), Data.size(), 0);
	
	if (retval < 0) {
		throw SocketException(errno);
	} else return retval;
}

byte* UNIXClientSocket::ReadBytes(int Bytes) {
	// Set up a buffer
	byte* buffer = static_cast<byte*>(malloc(sizeof(byte)*Bytes));
	
	// We'll use recv again, with MSG_WAITALL because we want to fill our quota
	// If we got -1, there's an error, throw an exception.
	if (recv(this->clientSocket, buffer, Bytes, MSG_WAITALL) < 0) {
		throw SocketException(errno);
	}
	
	// No need to massage the data, just send it back.
	// Just a note, this buffer does need to be free()'ed after use.
	return buffer;
}

std::string UNIXClientSocket::ReadLine() {
	// Again, we need to set up a buffer to read into, plus the extra byte
	// We'll allocate 4 KiB for the line, and hope it's not that long
	char* buffer = static_cast<char*>(malloc(sizeof(char)*LINE_BUFFER_SIZE));	
	
	// We'll use fgets this time, since we're largely mimicing its behavior
	buffer = fgets(buffer, sizeof(char)*LINE_BUFFER_SIZE, this->clientSocketDescriptor);
	
	// If fgets gave us back NULL, we've got a problem, bail out to the caller
	if (buffer == NULL) {
		throw SocketException(errno);
	}
	
	// Build the string and ship it back
	int bufferSize = strlen(buffer);
	if (buffer[bufferSize-1] == '\n') {
		buffer[bufferSize-1] = '\0';
	}
	std::string line(buffer);
	return line;
}

std::string UNIXClientSocket::Read(int ReadSize) {
	// First, set up a buffer to read into. That extra byte is for the null terminator.
	char* buffer = static_cast<char*>(malloc(sizeof(char)*ReadSize+1));
	
	// We'll use recv with the MSG_WAITALL flag because we want to be sure we get ReadSize bytes.
	// If we got -1, there's an error, throw an exception.
	if (recv(this->clientSocket, buffer, ReadSize+1, MSG_WAITALL) < 0) {
		throw SocketException(errno);
	}
	
	// Well, if we got this far, we got all the data we wanted.
	// Let's make it into a std::string and give it back.
	std::string returnString(buffer);
	return returnString;
}
